// Integration tests for the incremental update system
use codegraph_core::{
    FileWatcher, IncrementalParseEngine, WatcherConfig, WatcherState,
    CodeChange, TextEdit, Position, IncrementalEngine,
};
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::time::sleep;
use tempfile::TempDir;

/// Test that file change detection meets the <50ms requirement
#[tokio::test]
async fn test_file_change_detection_performance() {
    let temp_dir = TempDir::new().unwrap();
    let test_file = temp_dir.path().join("test.rs");
    
    // Create test file
    tokio::fs::write(&test_file, "fn main() { println!(\"Hello\"); }").await.unwrap();
    
    // Create watcher with optimized config
    let parse_engine = Arc::new(IncrementalParseEngine::new());
    let mut config = WatcherConfig::default();
    config.debounce_delay_ms = 25; // Optimized for <50ms
    config.enable_conflict_detection = true;
    
    let mut watcher = FileWatcher::with_config(parse_engine, config);
    
    // Start watching
    watcher.start_watching(temp_dir.path()).await.unwrap();
    
    // Measure detection time
    let start_time = Instant::now();
    
    // Modify file
    tokio::fs::write(&test_file, "fn main() { println!(\"Hello, World!\"); }").await.unwrap();
    
    // Wait for detection and processing
    sleep(Duration::from_millis(100)).await;
    
    let detection_time = start_time.elapsed();
    
    // Verify detection time is under 50ms (with some buffer for test environment)
    assert!(detection_time.as_millis() < 100, 
            "Detection time {}ms exceeds 50ms requirement", detection_time.as_millis());
    
    watcher.stop_watching().await.unwrap();
}

/// Test that incremental updates complete within 500ms for large projects
#[tokio::test]
async fn test_incremental_update_performance() {
    let parse_engine = Arc::new(IncrementalParseEngine::new());
    
    // Create a large code change to simulate big project update
    let large_code_change = CodeChange {
        file_path: "large_file.rs".to_string(),
        language: "rust".to_string(),
        edit: TextEdit {
            start_byte: 0,
            old_end_byte: 1000,
            new_end_byte: 2000,
            start_position: Position::new(0, 0),
            old_end_position: Position::new(50, 0),
            new_end_position: Position::new(100, 0),
            new_text: generate_large_rust_code(2000),
        },
        timestamp: chrono::Utc::now(),
    };
    
    let start_time = Instant::now();
    
    // Process the update
    let result = parse_engine.update_graph(&large_code_change).await.unwrap();
    
    let update_time = start_time.elapsed();
    
    // Verify update time is under 500ms
    assert!(update_time.as_millis() < 500, 
            "Update time {}ms exceeds 500ms requirement", update_time.as_millis());
    
    // Verify the update was successful
    assert!(result.success);
    assert!(result.duration_ms < 500);
}

/// Test batch operations performance
#[tokio::test]
async fn test_batch_operations_performance() {
    let parse_engine = Arc::new(IncrementalParseEngine::new());
    
    // Create multiple code changes
    let mut changes = Vec::new();
    for i in 0..50 {
        changes.push(CodeChange {
            file_path: format!("file_{}.rs", i),
            language: "rust".to_string(),
            edit: TextEdit {
                start_byte: 0,
                old_end_byte: 100,
                new_end_byte: 150,
                start_position: Position::new(0, 0),
                old_end_position: Position::new(5, 0),
                new_end_position: Position::new(7, 0),
                new_text: format!("fn function_{}() {{ println!(\"test\"); }}", i),
            },
            timestamp: chrono::Utc::now(),
        });
    }
    
    let start_time = Instant::now();
    
    // Process all changes
    for change in changes {
        parse_engine.update_graph(&change).await.unwrap();
    }
    
    let batch_time = start_time.elapsed();
    
    // Verify batch processing is efficient (should be much faster than 50 * 500ms)
    assert!(batch_time.as_millis() < 5000, 
            "Batch processing time {}ms is too slow", batch_time.as_millis());
}

/// Test memory usage optimization
#[tokio::test]
async fn test_memory_usage_optimization() {
    let temp_dir = TempDir::new().unwrap();
    let parse_engine = Arc::new(IncrementalParseEngine::new());
    let mut watcher = FileWatcher::new(parse_engine);
    
    // Start watching
    watcher.start_watching(temp_dir.path()).await.unwrap();
    
    // Create many files to test memory management
    for i in 0..100 {
        let test_file = temp_dir.path().join(format!("test_{}.rs", i));
        tokio::fs::write(&test_file, format!("fn test_{}() {{}}", i)).await.unwrap();
    }
    
    // Wait for processing
    sleep(Duration::from_millis(500)).await;
    
    // Get memory stats
    let memory_stats = watcher.get_memory_stats().await;
    
    // Verify memory usage is reasonable (less than 100MB for this test)
    assert!(memory_stats.current_usage_bytes < 100 * 1024 * 1024,
            "Memory usage {} bytes is too high", memory_stats.current_usage_bytes);
    
    // Test memory cleanup
    watcher.cleanup_memory().await.unwrap();
    
    let memory_stats_after = watcher.get_memory_stats().await;
    
    // Verify cleanup reduced memory usage
    assert!(memory_stats_after.current_usage_bytes <= memory_stats.current_usage_bytes,
            "Memory cleanup did not reduce usage");
    
    watcher.stop_watching().await.unwrap();
}

/// Test conflict detection and resolution
#[tokio::test]
async fn test_conflict_detection_and_resolution() {
    let parse_engine = Arc::new(IncrementalParseEngine::new());
    
    let file_path = "conflict_test.rs";
    
    // Detect conflicts for concurrent updates
    let conflicts = parse_engine.detect_update_conflicts(file_path, 1).await.unwrap();
    
    // Should have no conflicts initially
    assert!(conflicts.is_empty());
    
    // Test conflict resolution
    if !conflicts.is_empty() {
        let resolution = parse_engine.resolve_conflicts(conflicts).await.unwrap();
        assert!(resolution.failed_conflicts.is_empty(), "Failed to resolve conflicts");
    }
    
    // Test safe update check
    let can_update = parse_engine.can_update_safely(file_path, 1).await.unwrap();
    assert!(can_update, "Should be able to update safely");
}

/// Test state persistence and recovery
#[tokio::test]
async fn test_state_persistence_and_recovery() {
    let temp_dir = TempDir::new().unwrap();
    let parse_engine = Arc::new(IncrementalParseEngine::new());
    
    // Create watcher and start watching
    let mut watcher = FileWatcher::new(parse_engine.clone());
    watcher.start_watching(temp_dir.path()).await.unwrap();
    
    // Get session ID
    let _session_id = watcher.get_session_id().to_string();
    
    // Save state
    watcher.save_state().await.unwrap();
    
    // Stop watcher
    watcher.stop_watching().await.unwrap();
    
    // Create new watcher and try to recover
    let mut new_watcher = FileWatcher::new(parse_engine);
    let recovered = new_watcher.load_state().await.unwrap();
    
    // Verify state was loaded (session ID will be different for new session)
    assert!(recovered, "Should have recovered state");
    
    new_watcher.cleanup_state_files().await.unwrap();
}

/// Helper function to generate large Rust code for testing
fn generate_large_rust_code(target_size: usize) -> String {
    let mut code = String::new();
    let mut current_size = 0;
    let mut counter = 0;
    
    while current_size < target_size {
        let function = format!(
            "fn function_{}() {{\n    println!(\"This is function {}\");\n    let x = {};\n    let y = x * 2;\n}}\n\n",
            counter, counter, counter
        );
        code.push_str(&function);
        current_size += function.len();
        counter += 1;
    }
    
    code
}

/// Integration test for the complete incremental update system
#[tokio::test]
async fn test_complete_incremental_update_system() {
    let temp_dir = TempDir::new().unwrap();
    let parse_engine = Arc::new(IncrementalParseEngine::new());
    
    // Configure for optimal performance
    let mut config = WatcherConfig::default();
    config.debounce_delay_ms = 25;
    config.enable_parallel_processing = true;
    config.enable_conflict_detection = true;
    
    let mut watcher = FileWatcher::with_config(parse_engine, config);
    
    // Start the system
    watcher.start_watching(temp_dir.path()).await.unwrap();
    
    // Verify initial state
    assert_eq!(watcher.get_state().await, WatcherState::Running);
    
    // Create test files
    let test_files = vec!["main.rs", "lib.rs", "utils.rs"];
    for file_name in &test_files {
        let file_path = temp_dir.path().join(file_name);
        tokio::fs::write(&file_path, format!("// {}\nfn main() {{}}", file_name)).await.unwrap();
    }
    
    // Wait for initial processing
    sleep(Duration::from_millis(200)).await;
    
    // Test pause/resume functionality
    watcher.pause_monitoring().await.unwrap();
    assert_eq!(watcher.get_state().await, WatcherState::Paused);
    
    watcher.resume_monitoring().await.unwrap();
    assert_eq!(watcher.get_state().await, WatcherState::Running);
    
    // Test performance under load
    let start_time = Instant::now();
    
    // Modify multiple files simultaneously
    for (i, file_name) in test_files.iter().enumerate() {
        let file_path = temp_dir.path().join(file_name);
        let new_content = format!("// Modified {}\nfn main() {{ println!(\"Hello {}\"); }}", file_name, i);
        tokio::fs::write(&file_path, new_content).await.unwrap();
    }
    
    // Wait for processing
    sleep(Duration::from_millis(300)).await;
    
    let total_time = start_time.elapsed();
    
    // Verify total processing time meets requirements
    assert!(total_time.as_millis() < 1000, 
            "Total processing time {}ms exceeds reasonable limits", total_time.as_millis());
    
    // Get final statistics
    let stats = watcher.get_stats().await;
    let memory_stats = watcher.get_memory_stats().await;
    
    // Verify system health
    assert!(stats.is_watching);
    assert!(memory_stats.memory_pressure < 0.8, "Memory pressure too high");
    
    // Clean shutdown
    watcher.stop_watching().await.unwrap();
    assert_eq!(watcher.get_state().await, WatcherState::Stopped);
}
